#ifndef PARSINGARGS_H
#define PARSINGARGS_H
/* purpose @ 解析输入的参数，
 *          1、通过AddArgType将必须参数和可允许的参数key加入到判定列表中
 *          2、通过Parse中的result将结果返回，其中结果的key为合法的key，vecotr为参数列表
 *          3、参数列表支持去掉参数前后的引号和\对引号和\的转义
 *          4、可通过SetUniqArgs设置短关键字只能唯一出现的关键字,使用冒号(:)区分多个短关键字
 *          5、可通过SetMutexArgs设置不能同时出现的短关键字，使用冒号(:)区分多个短关键字
 *          6、可通过SetDependence设置短关键字间的依赖关系
 *          7、可通过SetNecessaryArgs设置必须要有的短关键字
 *
 *          特殊合法字段：
 *          格式               实际存储值
 *          \\value\"            \value"
 *          "\\\value\""         \value"
 *
 *          注意事项：
 *              1、输入参数列表中参数分隔以空格区分
 *              2、- 后跟单字符关键字，--后跟长字符串关键字
 *              3、关键字不能重复出现，长短关键字不能同时出现在参数列表，否则会Parse函数会提示参数错误
 *              4、关键字中不能出现:和|，只能是数字和字母
 *
 *          用法：
 *              ParsingArgs pa;
 *              pa.AddArgType("help","help", ParsingArgs::NO_VALUE);
 * //NO_VALUE关键字后不能有参数
 *              pa.AddArgType("h","host", ParsingArgs::MUST_VALUE); //必须有关键字
 *              pa.AddArgType("p","pppp", ParsingArgs::MAYBE_VALUE); //MAYBE_VALUE
 * 关键字后可能有关键字
 *              pa.AddArgType("version","version", ParsingArgs::NO_VALUE);
 *              pa.AddArgType("i","insert", ParsingArgs::NO_VALUE);
 *              pa.AddArgType("n","number", ParsingArgs::NO_VALUE);
 *              pa.AddArgType("g","get", ParsingArgs::NO_VALUE);
 *              pa.SetUniqArgs("help:version"); //只能唯一出现的关键字为help和version
 *              pa.SetMutexArgs("help:version:h");
 * //不能同时出现的关键字为help和version和h
 *              pa.SetDependence("h","g|i|n"); //h后必须跟g或i或n
 *              pa.SetDependence("g","k"); //g后必须有k关键字
 *              pa.SetDependence("i","k&v");//i后必须有关键子k和v
 *              std::map<std::string, std::vector<std::string> > result;
 *              std::string errInfo;
 *              int iRet = pa.Parse(tmpPara,result, errInfo);
 * //result以输入关键字为key存储相关的值序列
 *              int iRet = pa.Parse(argc, argv, result, errInfo);
 * date    @ 2014.02.19
 * author  @ haibin.wang
 *
 */
#include <list>
#include <map>
#include <string>
#include <vector>

class ParsingArgs {
    public:
    ParsingArgs();
    ~ParsingArgs();
    enum KeyFlag { INVALID_KEY = -1, NO_VALUE, MAYBE_VALUE, MUST_VALUE };

    /* pur @
     * 添加解释参数，一个参数可以是长参数，也可以是缩写的段参数，
     * 短参数可以为多个字符，longName和shortName至少要有一个
     * para @ shortName 短参数名,0为不要短参数
     * para @ longName 长参数名 ，NULL为不要长参数
     * para @ flag 是否需要参数，0不需要，1必须要，2可要可不要
     * para @ helpInfo 帮助显示的信息
     * return @ true 添加成功，false添加失败
    */
    bool AddArgType(const char *shortName,
                    const char *longName = NULL,
                    KeyFlag flag = NO_VALUE,
                    const char *helpInfo = NULL);

    /* pur @ 根据参数类型解释传入的字符串
     * para @ paras 需要解释的字符串
     * para @ result 返回解析后的结果
     * para @ errPos 当错误的时候返回出错的大概位置和错误信息
     * return @ 0 解释成功，负数 解释失败
     *          -1 未知参数错误
                -2 不能有参数的选项有参数错误
     *          -3 必有参数选项后没有跟参数
     *          -4 关键字没有加入到AddArgType中
     *          -5 关键字重复
     *          -6 没解析到任何参数
     *          -7 必有关键字没出现
     *          -8 唯一关键字和其他关键字同时出现
     *          -9 不能同时出现的关键字同时出现
     *          -10 依赖关系错误
    */
    int Parse(const std::string &paras,
              std::map<std::string, std::vector<std::string> > &result,
              std::string &errPos);

    /* pur @ 根据参数类型解释传入的字符串,此函数方便直接解析main参数内容
     * para @ paras 需要解释的字符串
     * para @ result 返回解析后的结果
     * para @ errPos 当错误的时候返回出错的大概位置
     * return @ 0 解释成功，负数 解释失败,错误说明同上
    */
    int Parse(int argc, char **argv,
            std::map<std::string, std::vector<std::string> > &result,
            std::string &errPos);

    /* pur @ 设置只能唯一出现的短关键字
     * para @ uniqs 短关键字序列，用冒号分隔多个短关键字
    */
    void SetUniqArgs(const std::string &uniqs);

    /* pur @ 设置不能同时出现的短关键字
     * para @ mutexes 互斥的短关键字序列，用冒号分隔多个短关键字
    */
    void SetMutexArgs(const std::string &mutexes);

    /* pur @ 设置不同短关键字间的依赖关系
     * para @ master 主短关键字，
     * para @ slave 从短关键字
     *          slave关键字可以有多个，
     *          如B&C代表master后必须出现B和C关键字，
     *          B|C代表master后必须出现B或C，
     *          B&C|D 代表master后必须出现B和C或出现D关键字
     * return @
    */
    void SetDependence(const std::string &master, const std::string &slave);

    /* pur @ 设置必须出现的关键字
     * para @ keys 必须要出现的短关键字列表
    */
    void SetNecessaryArgs(const std::string &keys);

    /*
     * pur @ 获取帮助信息
     * return @
     */
    const char *GetHelpInfo()
    {
        return m_helpInfo.c_str();
    }

    private:
    /* pur @ 判定传入的参数是否是已经添加的参数类型,如果是则去掉-或--,并返回
     * para @ bMustValue 是否已经确定word为value而不是key
     * para @ key 要判定的参数,
     * return @ -1 不是合法参数类型 否则返回Option中的flag
    */
    KeyFlag GetKeyFlag(const bool &bMustValue, std::string &word);

    /* pur @ 删除关键字前的-或--
    */
    void RemoveKeyFlag(std::string &paras);

    /* pur @ 从Paras中获取一个单词，自动过滤掉单词前后引号，
     * 并实现\对空格和引号的转义
     * para @ Paras 返回第一个单词后的所有内容
     * para @ word 返回第一单词
     * para @ bMustValue 返回分离的word是否一定是value，（根据引号判定）
     * return @ 成功返回true，false失败
     */
    bool GetWord(std::string &Paras, std::string &word, bool &bMustValue);

    /* pur @ 检查关键字是否重复
     * para @ key 被检查的关键字
     * para @  result已存储的关键字序列
     * return @ true 是重复的，false不重复
    */
    bool IsDuplicateKey(const std::string &key,
                        const std::map<std::string,
                        std::vector<std::string> > &result);

    std::vector<std::string> Split(const std::string &str,
            const std::string &pattern);

    /* pur @ 检查生成的关键字列表是否符合规范
     * para @ result 被检查的关键字序列
     * para @ errInfo 记录错误信息
     * return @ 0 符合规范，负数不符合设置的规则
    */
    int CheckRules(const std::map<std::string,
            std::vector<std::string> > &result,
            std::string &errInfo);

    struct Option {
        std::string m_longName;
        std::string m_shortName;
        KeyFlag m_flag;
    };

    std::string m_helpInfo;              //存放帮助信息
    std::vector<Option> m_args;          //参数信息列表
    std::vector<std::string> m_uniqArgs; //只能唯一出现的关键字列表
    std::vector<std::string> m_mustArgs; //必须要有的参数列表
    std::vector<std::vector<std::string> > m_mutexArgs; //互斥的短关键字列表
    std::map<std::string, std::list<std::vector<std::string> > > m_dependArgs; //关键字依赖关系,list存储|内容vecrot存储&内容
};

#endif
